using System;
using System.Collections.Generic;
using System.Data;
using CustomORM.Mapping;
using NUnit.Framework;

namespace CustomORM.Tests.CustomORM.Mapping
{
    [TestFixture]
    public class EntityToTableMapperTests
    {
        [Test]
        public void HasMany_AddingKid_AddsToChildrenDictionaryy()
        {
            var parentMapper = new EntityToTableMapperExposed<Parent>();

            parentMapper.HasMany(x => x.Kids, "PARENT_ID");
            Assert.That(parentMapper.ChildrenDictionary, Is.Not.Null);
            Assert.That(parentMapper.ChildrenDictionary.Count, Is.EqualTo(1));

            Parent parent = new Parent() { Kids = new List<Kid>() { new Kid() { Name = "Mango" } } };
            Assert.That(((List<Kid>)(parentMapper.ChildrenDictionary[0].ExecExpression.Compile().Invoke(parent)))[0].Name, Is.EqualTo("Mango"));
            Assert.That(parentMapper.ChildrenDictionary[0].ForceDeleteMissedItems, Is.EqualTo(false));
        }

        [Test]
        public void HasMany_AddingKid_MarkForDeletion_MappingExpressionGetsDeleteMissedItems()
        {
            var parentMapper = new EntityToTableMapperExposed<Parent>();

            parentMapper.HasMany(x => x.Kids, "PARENT_ID").DeleteMissedItems();
            Assert.That(parentMapper.ChildrenDictionary, Is.Not.Null);
            Assert.That(parentMapper.ChildrenDictionary.Count, Is.EqualTo(1));

            Parent parent = new Parent() { Kids = new List<Kid>() { new Kid() { Name = "Mango" } } };
            Assert.That(((List<Kid>)(parentMapper.ChildrenDictionary[0].ExecExpression.Compile().Invoke(parent)))[0].Name, Is.EqualTo("Mango"));
            Assert.That(parentMapper.ChildrenDictionary[0].ForceDeleteMissedItems, Is.EqualTo(true));
        }


        [Test]
        public void GeneratesSQL()
        {
            MagicMapper.RemoveMapper(typeof(Kid));
            var kidMapper = new EntityToTableMapperExposed<Kid>();
            kidMapper.Table("KID");
            kidMapper.Id(x => x.Id, "KID_ID");
            kidMapper.Map(x => x.ParentId, "PARENT_ID");
            kidMapper.Map(x => x.Name, "NAME");

            string sqlQuery = kidMapper.GenerateSelectSQL();
            Assert.That(sqlQuery.ToLower(), Is.EqualTo("select kid_id, parent_id, name from kid"));

            string selectSqlWithWhereOnId = kidMapper.GenerateSelectSQL(1);
            Assert.That(selectSqlWithWhereOnId.ToLower(), Is.EqualTo("select kid_id, parent_id, name from kid where kid_id = 1"));

            string selectSqlWithWhere = kidMapper.GenerateSimpleSelectSQL("PARENT_ID", 7);
            Assert.That(selectSqlWithWhere.ToLower(), Is.EqualTo("select kid_id, parent_id, name from kid where parent_id = 7"));
        }

        [Test]
        public void GetKeyValue_FetchesId_AsExpected()
        {
            // Setup mapping
            MagicMapper.RemoveMapper(typeof(Kid));
            var kidMapper = new EntityToTableMapperExposed<Kid>();
            kidMapper.Table("KID");
            kidMapper.Id(x => x.Id, "KID_ID");
            kidMapper.Map(x => x.ParentId, "PARENT_ID");

            var kid = new Kid() {Id = 999, Name = "Blah"};
            var keyValue = kidMapper.GetKeyValue(kid);

            Assert.That(keyValue,Is.EqualTo(999));
        }

        [Test]
        public void MapToEntityList_TakesDataTable_ListOfInstancesOfClassExpected()
        {
            var johnBirthdate = new DateTime(2000, 1, 1);
            var annaBirthdate = new DateTime(2002, 1, 1);
            var johnId = 7;
            var annaId = 8;

            // Setup mapping
            MagicMapper.RemoveMapper(typeof(Kid));
            var kidMapper = new EntityToTableMapperExposed<Kid>();
            kidMapper.Table("KID");
            kidMapper.Id(x => x.Id, "KID_ID");
            kidMapper.Map(x => x.ParentId, "PARENT_ID");
            kidMapper.Map(x => x.Name, "NAME");
            kidMapper.Map(x => x.BirthDate, "BIRTH_DATE");

            // setup data
            var table = new DataTable();
            table.Columns.Add("KID_ID", typeof(int));
            table.Columns.Add("PARENT_ID", typeof(int));
            table.Columns.Add("NAME", typeof(string));
            table.Columns.Add("BIRTH_DATE", typeof(DateTime));

            
            table.LoadDataRow(new object[] { johnId, 1, "John", johnBirthdate }, true);
            table.LoadDataRow(new object[] { annaId, 1, "Anna", annaBirthdate }, true);
            List<Kid> kids = (List<Kid>)kidMapper.MapToEntityList(table);

            Assert.That(kids, Is.Not.Null);
            Assert.That(kids.Count, Is.EqualTo(2));
            Kid john = kids[0];
            Assert.That(john.Id, Is.EqualTo(johnId));
            Assert.That(john.ParentId, Is.EqualTo(1));
            Assert.That(john.Name, Is.EqualTo("John"));
            Assert.That(john.BirthDate, Is.EqualTo(johnBirthdate));
            Kid anna = kids[1];
            Assert.That(anna.Id, Is.EqualTo(annaId));
            Assert.That(anna.ParentId, Is.EqualTo(1));
            Assert.That(anna.Name, Is.EqualTo("Anna"));
            Assert.That(anna.BirthDate, Is.EqualTo(annaBirthdate));
        }

        public class Parent
        {
            public int Id { get; set; }
            public string Name { get; set; }

            public List<Kid> Kids { get; set; }
        }

        public class Kid
        {
            public int Id { get; set; }
            public int ParentId { get; set; }
            public string Name { get; set; }
            public DateTime BirthDate { get; set; }
        }
    }
}